<template>
  <div class="picture-card">
    <div class="picture-card-list">
      <div class="picture-card__item" v-for="(item, index) in fileListData" :key="item.fileId">
        <div
          class="picture-card__item-play"
          v-if="fileType === 'video' && multipartOssStore.loadingArr[item.fileId] === 100"
          @click="handlePreview(item)"
        >
          <svg-icon iconClass="play-icon" color="#fff" />
        </div>
        <div class="picture-card__item-del" v-if="!disabled">
          <el-icon @click="handleRemove(index)"><Close /></el-icon>
        </div>
        <template v-if="multipartOssStore.loadingArr[item.fileId] === 100">
          <el-image
            v-if="fileType === 'image'"
            :src="item.fileUrl"
            fit="cover"
            preview-teleported
            :preview-src-list="[item.fileUrl]"
          ></el-image>
          <video v-if="fileType === 'video'" :src="item.fileUrl"></video>
        </template>
        <el-progress
          v-if="multipartOssStore.loadingArr[item.fileId] !== 100"
          type="circle"
          :percentage="multipartOssStore.loadingArr[item.fileId]"
        />
      </div>
      <div class="picture-card__item" style="border: 0" v-if="!disabled && fileListData.length < maxLength">
        <el-upload
          ref="upload"
          action=""
          :file-list="fileListData"
          :accept="accept"
          :show-file-list="false"
          :multiple="multiple"
          :limit="maxLength"
          :before-upload="beforeUpload"
          :on-exceed="oneExceed"
          :http-request="customUpload"
          :on-error="onError"
        >
          <div class="upload-picture-card">
            <el-icon><Plus /></el-icon>
            <span class="upload__tip">点击上传</span>
          </div>
        </el-upload>
      </div>
    </div>
    <div v-if="!disabled" slot="tip" class="upload__tip">{{ tip }}</div>

    <div class="preview-dialog">
      <el-dialog title="视频预览" v-model="isPreview">
        <video v-if="isPreview" :src="previewVideoUrl" controls></video>
      </el-dialog>
    </div>
  </div>
</template>

<script setup>
/**
 * TODO:图片视频上传组件，后续优化可以和AliOssUpload组件合并,封装完整的附件、图片、视频上传预览组件
 */
import { computed, getCurrentInstance, ref, watch } from "vue";
import { dayjs } from "element-plus";
import { Plus, Close } from "@element-plus/icons-vue";
import { fileReadRole, setACL, signatureUrl } from "@/utils/aliOssUpload";
import useMultipartOssStore from "@/store/modules/multipartOss";
import defaultConfig from "@/settings";
import { getVideoTime } from "./utils";

const props = defineProps({
  fileList: {
    type: Array,
    default: () => []
  },
  fileType: {
    type: String,
    default: "image"
  },
  itemSize: {
    type: String,
    default: "84px"
  },
  accept: {
    type: String,
    default: ".jpg"
  },
  maxSize: {
    type: Number,
    default: 10
  },
  maxLength: {
    type: Number,
    default: 9
  },
  tip: {
    type: String,
    default: ""
  },
  disabled: Boolean,
  multiple: Boolean,
  isLimitDuration: Boolean,
  limitDuration: {
    type: Number,
    default: 60
  }
});
const emits = defineEmits(["uploadSuccess", "update:modelValue", "uploadRemove"]);
const { proxy } = getCurrentInstance();
const multipartOssStore = useMultipartOssStore();

const fileListData = ref([]);
watch(
  () => props.fileList,
  newVal => {
    props.fileList.forEach(item => {
      item.fileUrl && (multipartOssStore.loadingArr[item.fileId] = 100);
    });
    fileListData.value = [...newVal];
  },
  { immediate: true, deep: true }
);

/**
 * TODO:限制文件类型，还可以在优化，目前文件类型必须传入组件
 */
const acceptTip = computed(() => {
  return props.accept?.replace(/\./g, "")?.replace(/,/g, "/") || "";
});
const beforeUpload = async file => {
  const suffix = file.name?.slice(file.name.lastIndexOf(".")) || "";
  const fileTypes = props.accept?.split(",") || [];
  const maxSize = file.size / 1024 / 1024 < 10;
  const isInFileType = fileTypes.includes(suffix);
  if (!isInFileType) {
    proxy.$modal.msgError(`只能上传${acceptTip.value}类型的文件!`);
    return false;
  }
  if (!maxSize) {
    proxy.$modal.msgError(`单个文件大小不能超过${props.maxSize}M!`);
    return false;
  }
  if (props.isLimitDuration) {
    const duration = await getVideoTime(file);
    if (duration > props.limitDuration) {
      proxy.$modal.msgError(`单个文件大小不能超过${props.limitDuration}秒!`);
      return false;
    }
  }
  return true;
};
const oneExceed = () => {
  proxy.$modal.msgError(`最多上传${props.maxLength}个文件`);
  return false;
};
const getSuffix = fileName => {
  return fileName.slice(fileName.lastIndexOf(".") + 1);
};
const getFileName = fileName => {
  return fileName.slice(0, fileName.lastIndexOf("."));
};
const fileToBlob = file => {
  return {
    fileId: file.uid,
    uid: file.uid,
    name: file.name,
    size: file.size,
    type: file.type,
    suffix: file.suffix,
    fileUrl: URL.createObjectURL(file)
  };
};
const customUpload = async option => {
  fileListData.value.push(fileToBlob(option.file));
  multipartOssStore.loadingArr[option.file.uid] = 0;
  const nowTime = dayjs().format("YYYY-MM-DD HH:mm:ss");
  let file = option.file,
    fileName = option.file.name;
  fileName = `${getFileName(fileName)}-${nowTime}.${getSuffix(fileName)}`;
  file = new File([file], fileName, { type: file.type });
  let objName = `${defaultConfig.fileFolder}/${file.name}`;
  multipartOssStore
    .submit(objName, file, option.file.uid)
    .then(async res => {
      await setACL(objName, fileReadRole.publicRead);
      const fileUrl = await signatureUrl(objName);
      res.fileUrl = fileUrl ? `${fileUrl}`.split("?")[0] : "";
      res.fileId = option.file.uid;
      res.fileName = file.name;
      emits("uploadSuccess", res);
    })
    .catch(err => {
      console.log(err);
      fileListData.value.pop();
      alert(JSON.stringify(err));
    });
};
const onError = () => {};
const handleRemove = index => {
  emits("uploadRemove", index);
};

const isPreview = ref(false);
const previewVideoUrl = ref("");
const handlePreview = item => {
  isPreview.value = true;
  previewVideoUrl.value = item.fileUrl;
};
</script>

<style lang="scss" scoped>
$picture-card-size: 84px;
.design {
  i {
    padding: 10px;
    font-size: xx-large;
    background: white;
    border: 1px dashed #8c8c8c;
  }
}

.upload-picture-card {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  width: v-bind("props.itemSize");
  height: $picture-card-size;
  border: 1px solid #cdd0d6;
  background-color: #fafafa;
  overflow: hidden;
  :deep(.el-icon) {
    margin-bottom: 8px;
    font-size: 24px;
    color: #c9cdd4;
  }
}
.picture-card {
  display: flex;
  display: -webkit-flex;
  flex-wrap: wrap;
  width: 100%;
  .picture-card-list {
    width: 100%;
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(v-bind("props.itemSize"), 1fr));
    grid-gap: 10px;
  }
  &__item {
    display: flex;
    //justify-content: center;
    align-items: center;
    position: relative;
    width: 100%;
    height: $picture-card-size;
    cursor: pointer;

    &-play {
      position: absolute;
      top: 50%;
      left: 50%;
      z-index: 9;
      transform: translate(-50%, -50%);
      :deep(.svg-icon) {
        font-size: 29px;
      }
    }
    &-del {
      display: flex;
      justify-content: center;
      align-items: center;
      position: absolute;
      right: -7px;
      top: -4px;
      z-index: 9;
      height: 16px;
      width: 16px;
      border-radius: 50%;
      background: red;
      color: #fff;
      outline: 2px solid;
    }

    :deep(.el-image),
    video {
      width: 100%;
      height: 100%;
      border-radius: 4px;
      overflow: hidden;
    }
    video {
      object-fit: cover;
    }
  }
}

:deep(.el-progress-circle) {
  width: 84px !important;
  height: 84px !important;
}
.preview-dialog {
  :deep(.el-dialog) {
    video {
      width: 100%;
      height: 50vh !important;
    }
  }
}

.upload__tip {
  font-size: 12px;
  color: #86909c;
  line-height: 22px;
}
</style>
